package com.tang.auth.domain.service.impl;

import cn.dev33.satoken.secure.SaSecureUtil;
import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
import com.alibaba.fastjson.JSON;
import com.google.gson.Gson;
import com.tang.auth.common.constants.AuthConstant;
import com.tang.auth.common.enums.AuthUserStatusEnum;
import com.tang.auth.domain.convert.AuthUserBOConverter;
import com.tang.auth.domain.entity.AuthUserBO;
import com.tang.auth.domain.service.AuthUserDomainService;
import com.tang.auth.infra.basic.entity.*;
import com.tang.auth.infra.basic.service.*;
import com.tang.common.enums.DeleteEnum;
import com.tang.common.exception.ServiceException;
import com.tang.common.redis.AuthKeyBuilder;
import com.tang.common.redis.CaptchaKeyBuilder;
import com.tang.component.redis.RedisUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

@Slf4j
@Service
public class AuthUserDomainServiceImpl implements AuthUserDomainService {
    public static final String DEFAULT_AVATAR = "https://tupian.qqw21.com/article/UploadPic/2019-8/2019812345950693.jpeg";
    @Resource
    private AuthUserService authUserService;

    @Resource
    private AuthUserRoleService authUserRoleService;

    @Resource
    private AuthRolePermissionService authRolePermissionService;

    @Resource
    private AuthPermissionService authPermissionService;

    @Resource
    private AuthRoleService authRoleService;

    @Resource
    private RedisUtil redisUtil;

    @Resource
    private ApplicationContext applicationContext;

    /**
     * 盐值
     *
     * @date 2024/2/15 10:59
     **/
    private final String salt = "tang_salt";

    /**
     * 用户注册
     *
     * @param authUserBO 注册信息
     * @return java.lang.Boolean
     * @author Tang
     * @date 2024/2/15 8:54
     **/
    @Override
    @SneakyThrows
    @Transactional(rollbackFor = Exception.class)
    public Boolean register(AuthUserBO authUserBO) {
        try {
            if (log.isInfoEnabled()) {
                log.info("com.tang.auth.domain.service.impl.AuthUserDomainServiceImpl.register.bo:{}", JSON.toJSONString(authUserBO));
            }
            AuthUser authUser = AuthUserBOConverter.INSTANCE.convertBOToEntity(authUserBO);
            //重复用户名检测
            checkUserNameIfOnly(authUser);
            //用户基本信息处理并添加信息到数据库
            handleBasicInfo(authUser);
            //给用户关联角色
            linkedRole(authUser);
            //将权限添加到redis
            addPermissionToRedis(authUser, AuthConstant.NORMAL_USER);
            return Objects.nonNull(authUser.getId());
        } catch (Exception e) {
            log.error("com.tang.auth.domain.service.impl.AuthUserDomainServiceImpl.register.error：{}", authUserBO, e);
            throw new ServiceException(e.getMessage());
        }
    }

    private void checkUserNameIfOnly(AuthUser authUser) {
        AuthUser search = new AuthUser();
        search.setUserName(authUser.getUserName());
        List<AuthUser> list = authUserService.queryByCondition(search);
        if (!list.isEmpty()) {
            throw new ServiceException("该账号已被占用");
        }
    }

    /**
     * 将用户对应角色的权限列表缓存至redis
     *
     * @param authUser 用户信息
     * @param role     角色名
     * @author Tang
     * @date 2024/2/16 19:52
     * @see AuthConstant role的枚举取值
     **/
    private void addPermissionToRedis(AuthUser authUser, String role) {
        //获取角色id
        AuthRole search = new AuthRole();
        search.setRoleKey(role);
        search.setIsDeleted(DeleteEnum.UN_DELETED.getCode());
        AuthRole authRole = authRoleService.queryRoleByCondition(search);
        //查询角色与权限信息
        AuthRolePermission authRolePermission = new AuthRolePermission();
        authRolePermission.setRoleId(authRole.getId());
        //查询角色权限关联列表
        List<AuthRolePermission> rolePermissionList = authRolePermissionService.queryByCondition(authRolePermission);
        String roleKey = AuthKeyBuilder.getRoleKey(String.valueOf(authUser.getUserName()));
        List<String> roleList = new LinkedList<>();
        roleList.add(authRole.getRoleKey());
        redisUtil.set(roleKey, new Gson().toJson(roleList));
        //权限id列表
        List<Long> permissionIds = rolePermissionList.stream()
                .map(AuthRolePermission::getPermissionId)
                .collect(Collectors.toList());
        //查询权限列表
        List<AuthPermission> authPermissions = authPermissionService.queryByIds(permissionIds);
        List<String> authPermissionKeys = authPermissions.stream()
                .map(AuthPermission::getPermissionKey)
                .collect(Collectors.toList());
        //将权限列表写入redis
        String permissionKey = AuthKeyBuilder.getPermissionKey(String.valueOf(authUser.getUserName()));
        redisUtil.set(permissionKey, new Gson().toJson(authPermissionKeys));
    }

    /**
     * 关联角色信息
     *
     * @param authUser 用户信息
     * @author Tang
     * @date 2024/2/15 13:59
     **/
    private void linkedRole(AuthUser authUser) {
        //用户角色信息关联
        //获取角色id
        AuthRole search = new AuthRole();
        search.setRoleKey(AuthConstant.NORMAL_USER);
        search.setIsDeleted(DeleteEnum.UN_DELETED.getCode());
        AuthRole authRole = authRoleService.queryRoleByCondition(search);
        if (Objects.isNull(authRole)) {
            throw new IllegalArgumentException("authRole is null,search condition is:" + JSON.toJSONString(search));
        }
        Long roleId = authRole.getId();
        //获取角色id
        Long userId = authUser.getId();
        //给用户关联角色id
        AuthUserRole authUserRole = new AuthUserRole();
        authUserRole.setUserId(userId);
        authUserRole.setRoleId(roleId);
        authUserRole.setIsDeleted(DeleteEnum.UN_DELETED.getCode());
        authUserRoleService.insert(authUserRole);
    }

    /**
     * 保存用户基本信息
     *
     * @param authUser 用户信息
     * @author Tang
     * @date 2024/2/15 13:59
     **/
    private void handleBasicInfo(AuthUser authUser) {
        //密码处理
        if (StringUtils.isNoneBlank(authUser.getPassword())) {
            authUser.setPassword(SaSecureUtil.md5BySalt(authUser.getPassword(), salt));
        }
        authUser.setAvatar(DEFAULT_AVATAR);
        //状态处理
        authUser.setStatus(AuthUserStatusEnum.OPEN.getCode());
        //用户删除标志位
        authUser.setIsDeleted(DeleteEnum.UN_DELETED.getCode());
        //插入用户数据
        authUserService.insert(authUser);
    }

    /**
     * 用户更新信息
     *
     * @param authUserBO 更新信息
     * @return java.lang.Boolean
     * @author Tang
     * @date 2024/2/15 9:21
     **/
    @Override
    @SneakyThrows
    public Boolean update(AuthUserBO authUserBO) {
        if (log.isInfoEnabled()) {
            log.info("com.tang.auth.domain.service.impl.AuthUserDomainServiceImpl.update.bo:{}", JSON.toJSONString(authUserBO));
        }
        //查询用户id
        AuthUser search = new AuthUser();
        search.setUserName(authUserBO.getUserName());
        AuthUser searchId = authUserService.queryByCondition(search).stream().findFirst().orElse(null);
        if (Objects.isNull(searchId) || Objects.isNull(searchId.getId())) {
            return false;
        }
        AuthUser authUser = AuthUserBOConverter.INSTANCE.convertBOToEntity(authUserBO);
        authUser.setId(searchId.getId());
        authUserService.update(authUser);
        return true;
    }

    /**
     * 删除用户信息
     *
     * @param authUserBO 待删除的用户
     * @return java.lang.Boolean
     * @author Tang
     * @date 2024/2/15 9:36
     **/
    @Override
    @SneakyThrows
    public Boolean delete(AuthUserBO authUserBO) {
        if (log.isInfoEnabled()) {
            log.info("com.tang.auth.domain.service.impl.AuthUserDomainServiceImpl.delete.bo:{}", JSON.toJSONString(authUserBO));
        }
        AuthUser authUser = AuthUserBOConverter.INSTANCE.convertBOToEntity(authUserBO);
        authUser.setIsDeleted(DeleteEnum.DELETE.getCode());
        authUserService.update(authUser);
        return true;
    }

    /**
     * 修改用户的状态
     *
     * @param authUserBO 用户信息
     * @return java.lang.Boolean
     * @author Tang
     * @date 2024/2/15 9:42
     **/
    @Override
    @SneakyThrows
    public Boolean changeStatus(AuthUserBO authUserBO) {
        if (log.isInfoEnabled()) {
            log.info("com.tang.auth.domain.service.impl.AuthUserDomainServiceImpl.changeStatus.bo:{}", JSON.toJSONString(authUserBO));
        }
        AuthUser authUser = AuthUserBOConverter.INSTANCE.convertBOToEntity(authUserBO);
        authUser.setStatus(AuthUserStatusEnum.of(authUserBO.getStatus()).getCode());
        authUserService.update(authUser);
        return true;
    }

    /**
     * 用户登录
     *
     * @param validCode 验证码
     * @author Tang
     * @date 2024/2/17 14:53
     **/
    @Override
    public SaTokenInfo doLogin(String validCode) {
        //获取openId并删除redis中的信息
        String captchaKey = CaptchaKeyBuilder.getCaptchaKey(validCode);
        String openId = redisUtil.get(captchaKey);
        redisUtil.del(captchaKey);
        if (StringUtils.isBlank(openId)) {
            throw new ServiceException("验证码无效");
        }
        AuthUserBO authUserBO = new AuthUserBO();
        authUserBO.setUserName(openId);
        try {
            //解决同源调用问题，防止事务不生效
            AuthUserDomainService userDomainService = applicationContext.getBean(AuthUserDomainService.class);
            userDomainService.register(authUserBO);
        } catch (Exception e) {
            log.info("用户已注册。直接登录");
        }
        //用户登录
        StpUtil.login(openId);
        return StpUtil.getTokenInfo();
    }

    /**
     * 获取用户信息
     *
     * @param authUserBO 查询条件
     * @return com.tang.auth.domain.entity.AuthUserBO
     * @author Tang
     * @date 2024/2/18 14:09
     **/
    @Override
    public AuthUserBO getUserInfo(AuthUserBO authUserBO) {
        AuthUser authUser = AuthUserBOConverter.INSTANCE.convertBOToEntity(authUserBO);
        List<AuthUser> authUsers = authUserService.queryByCondition(authUser);
        if (CollectionUtils.isEmpty(authUsers)) {
            return new AuthUserBO();
        }
        AuthUser res = authUsers.get(0);

        return AuthUserBOConverter.INSTANCE.convertEntityToBO(res);
    }

    /**
     * 用户退出登录
     *
     * @param userName 用户名
     * @author Tang
     * @date 2024/2/18 14:18
     **/
    @Override
    public void logout(String userName) {
        StpUtil.logout(userName);
    }
}
